package spring.parsing;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Element;
import spring.annotations.Component;
import spring.dao.factory.FactoryBean;
import spring.dao.factory.InitializingBean;
import spring.exceptions.BeanInitializationException;
import spring.factory.BeanFactory;
import spring.factory.SingletonBeanRegistry;
import spring.resources.DomElementHolder;
import utils.LogUtils;
import utils.ProxyUtils;
import utils.ReflectUtils;

import java.util.List;
import java.util.Objects;

public class XmlBeanParser extends AbstractXmlParser {


    public XmlBeanParser(SingletonBeanRegistry singletonBeanRegistry) {
        super(singletonBeanRegistry);
    }

    @Override
    public XmlNodeType getType() {
        return XmlNodeType.BEAN;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public void parse(Element element) {
        String id = element.attributeValue("id");
        String cls = element.attributeValue("class");
        if (singletonBeanRegistry.containsSingleton(id)) {
            return;
        }
        try {
            Class<?> beanClass = Class.forName(cls);
            Object beanInstance = beanClass.getConstructor().newInstance();
            List<Element> propertyElements = element.elements();
            boolean isFactoryBean = beanInstance instanceof FactoryBean;
            boolean isInitializingBean = beanInstance instanceof InitializingBean;
            Component component = beanInstance.getClass().getAnnotation(Component.class);
            if (id.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) || !isFactoryBean || Objects.nonNull(component)) {
                setBeanProperties(beanInstance, propertyElements);
                if (beanInstance instanceof FactoryBean) {
                    singletonBeanRegistry.registerSingleton(id, beanInstance);
                } else {
                    // 创建当前 bean 的实例代理对象
                    Object beanInstanceProxy = ProxyUtils.createProxyObject(beanInstance);
                    // 保存到单例池
                    singletonBeanRegistry.registerSingleton(id, beanInstanceProxy);
                }
            } else {
                FactoryBean<?> factoryBean = (FactoryBean) beanInstance;
                setBeanProperties(factoryBean, propertyElements);
                // 保存到单例池
                singletonBeanRegistry.registerSingleton(id, factoryBean);
            }
            if (isInitializingBean) {
                ((InitializingBean) beanInstance).afterPropertiesSet();
            }
        } catch (Exception e) {
            LogUtils.error("id = %s, class = %s，配置错误。", id, cls);
            throw new BeanInitializationException(e);
        }
    }

    public void setBeanProperties(Object beanInstance, List<Element> propertyElements) throws Exception {
        for (Element element : propertyElements) {
            String name = element.attributeValue("name");
            String ref = element.attributeValue("ref");
            String value = element.attributeValue("value");
            if (StringUtils.isBlank(name)) {
                throw new BeanInitializationException("属性的名称不能为空。");
            }
            if (StringUtils.isBlank(ref) && StringUtils.isBlank(value)) {
                throw new BeanInitializationException(String.format("属性：%s的值或引用关系必须配置。", name));
            }
            // 如果当前配置的是引用关系则处理引用关系逻辑，否则处理设值逻辑
            if (StringUtils.isBlank(value)) {
                // 否则执行ref对象的创建
                registerRefPropertyBean(beanInstance, name, ref);
            } else {
                // 否则执行value值的设置
                registerSingletonBean(beanInstance, name, value);
            }
        }
    }

    /**
     * 注册引用属性值单例 bean 对象
     *
     * @param beanInstance 依赖 bean 的源对象
     * @param name         属性名
     * @param ref          引用对象名称
     * @throws Exception 注册过程产生错误时抛出该异常
     */
    private void registerRefPropertyBean(Object beanInstance, String name, String ref) throws Exception {
        if (!singletonBeanRegistry.containsSingleton(ref)) {
            parse(DomElementHolder.getElement(ref));
        }
        Object refBeanInstance = singletonBeanRegistry.getSingleton(ref);
        if (refBeanInstance instanceof FactoryBean) {
            refBeanInstance = ((FactoryBean<?>) refBeanInstance).getObject();
        }
        ReflectUtils.setSimpleParameterValue(beanInstance, name, refBeanInstance);
    }

    /**
     * 注册简单属性值单例 bean 对象
     *
     * @param beanInstance 依赖 bean 的源对象
     * @param name         属性名
     * @param value        属性值
     * @throws Exception 注册过程产生错误时抛出该异常
     */
    private void registerSingletonBean(Object beanInstance, String name, String value) throws Exception {
        Class<?> parameterClass = ReflectUtils.getParameterClass(beanInstance, name);
        // 设置 value 属性值
        if (ReflectUtils.isPrimitive(parameterClass)) {
            // 基本类型
            ReflectUtils.setSimpleParameterValue(beanInstance, name, value);
        } else {
            // 对象类型实例
            Object parameterInstance = ReflectUtils.createObject(value);
            ReflectUtils.setSimpleParameterValue(beanInstance, name, parameterInstance);
        }
    }
}
